package org.apache.ibatis.session;

import org.apache.ibatis.binding.MapperRegistry;
import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.loader.ProxyFactory;
import org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMap;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.plugin.InterceptorChain;
import org.apache.ibatis.reflection.DefaultReflectorFacotry;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.apache.ibatis.scripting.LanguageDriverRegistry;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeAliasRegistry;
import org.apache.ibatis.type.TypeHandlerRegistry;

import java.util.*;
import java.util.function.BiFunction;

/**
 * 全局配置类
 * @author xieqiaofu
 * Create time: 2019/10/09
 **/
public class Configuration {

    /**
     * 一、new Configuration对象时的默认属性初始化
     */
    //1、安全的结果处理开关
    protected boolean safeResultHandlerEnabled = true;
    //2、多结果集开关
    protected boolean multipleResultSetsEnabled = true;
    //3、启用列标签
    protected boolean useColumnLabel = true;
    //4、启用缓存
    protected boolean cacheEnabled = true;
    //5、用实际的参数名称
    protected boolean useActualParamName = true;
    //6、本地缓存默认在一次session中
    protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
    //7、当值为null时，默认jdbcType为other
    protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
    //8、懒加载时触发的方法
    protected Set<String> lazyLoadTriggerMethods = new HashSet<>(Arrays.asList("equals", "clone", "hashCode", "toString"));
    //9、默认的执行类型
    protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
    //10、默认映射没有在内部定义嵌套结果映射的结果
    protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
    //11、当列和属性不匹配时选择的策略
    protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;
    //12、懒加载开关默认为false
    protected boolean lazyLoadingEnabled = false;

    /**
     * 二、new Configuration对象时的默认对象属性初始化
     */
    //1、初始化一个空的用户可配置参数的properties配置文件
    protected Properties variables = new Properties();
    //2、初始化默认的反射工具类
    protected ReflectorFactory reflectorFactory = new DefaultReflectorFacotry();
    //3、用户创建任何对象的默认工具类
    protected ObjectFactory objectFactory = new DefaultObjectFactory();
    //4、
    protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
    //5、默认的动态代理生成类
    protected ProxyFactory proxyFactory = new JavassistProxyFactory();
    //6、mapper注册类， 持有Configuration对象
    protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
    //7、mybatis插件管理类
    protected final InterceptorChain interceptorChain = new InterceptorChain();
    //8、类型转换注册类
    protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
    //9、类型别名映射注册类初始化
    protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
    //10、语言驱动注册类
    protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
    //11、缓存MappedStatement的容器
    protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")
            .conflictMessageProducer((savedValue, targetValue) ->
                    ".please check " + savedValue.getResource() + " and " + targetValue.getResource());
    //12、mybatis缓存的容器
    protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");
    //13、缓存ResultMap的容器
    protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");
    //14、缓存ParameterMap的容器
    protected final Map<String, ParameterMap> parameterMaps = new StrictMap<>("Parameter Maps collection");
    //15、缓存主键生成器
    protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<>("Key Generators collection");
    //16、资源路径缓存
    protected final Set<String> loadedResources = new HashSet<>();
    //17、缓存sql脚本xml节点
    protected final Map<String, XNode> sqlFragments = new StrictMap<>("XML fragments parsed from previous mappers");

    protected final Collection<XMLStatementBuilder> incompletesStatements = new LinkedList<>();
    protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<>();
    protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<>();
    protected final Collection<MethodResolver> incompleteMethods = new LinkedList<>();
    protected final Map<String, String> cacheRefMap = new HashMap<>();

    public Configuration() {
        typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    }

    public TypeAliasRegistry getTypeAliasRegistry() {
        return this.typeAliasRegistry;
    }

    public TypeHandlerRegistry getTypeHandlerRegistry() {
        return this.typeHandlerRegistry;
    }

    protected static class StrictMap<V> extends HashMap<String, V> {

        private static final long serialVersionUID = 8691673567953165464L;
        private final String name;
        private BiFunction<V, V, String> conflictMessageProducer;

        public StrictMap(String name, int initialCapacity, float loadFactor) {
            super(initialCapacity, loadFactor);
            this.name = name;
        }

        public StrictMap(String name, int initialCapacity) {
            super(initialCapacity);
            this.name = name;
        }

        public StrictMap(String name) {
            super();
            this.name = name;
        }

        public StrictMap(String name, Map<String, ? extends V> m) {
            super(m);
            this.name = name;
        }

        public StrictMap<V> conflictMessageProducer(BiFunction<V, V, String> conflictMessageProducer) {
            this.conflictMessageProducer = conflictMessageProducer;
            return this;
        }

        @Override
        public V put(String key, V value) {
            if(super.containsKey(key)) {
                throw new IllegalArgumentException(name + "already contains value for " + key
                    + (conflictMessageProducer == null ? "" : conflictMessageProducer.apply(super.get(key), value)));
            }
            if(super.containsKey(".")) {
                final String shortKey = getShortName(key);
                if(super.get(shortKey) == null) {
                    super.put(shortKey, value);
                } else {
                    super.put(shortKey, (V)new Ambiguity(shortKey));
                }
            }
            return super.put(key, value);
        }

        protected static class Ambiguity {
            final private String subject;

            public Ambiguity(String subject) {
                this.subject = subject;
            }

            public String getSubject() {
                return subject;
            }
        }

        private String getShortName(String key) {
            final String[] keyParts = key.split("\\.");
            return keyParts[keyParts.length - 1];
        }
    }
}
